package top.chukongxiang.monitorservice;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.setting.yaml.YamlUtil;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import top.chukongxiang.monitorservice.config.ApplicationState;
import top.chukongxiang.monitorservice.config.ApplicationStatusContextHolder;
import top.chukongxiang.monitorservice.javafx.MonitorSystemTray;
import top.chukongxiang.monitorservice.javafx.core.FxUncaughtExceptionHandler;
import top.chukongxiang.monitorservice.model.constant.ConfigKeyConstant;
import top.chukongxiang.monitorservice.tools.H2Util;

@Slf4j
@SpringBootApplication
@MapperScan("top.chukongxiang.monitorservice.mapper")
@EnableWebSocketMessageBroker
public class MonitorServiceApplication extends Application implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

    public static ConfigurableApplicationContext applicationContext;
    private static String[] runArgs;

    public static void main(String[] args) {
        runArgs = args;
        Application.launch(MonitorServiceApplication.class, args);
    }

    public static void startSpring() {
        try {
            log.info("开始启动 Spring");
            ApplicationStatusContextHolder.setState(ApplicationState.STARTING);
            SpringApplicationBuilder springApplicationBuilder = new SpringApplicationBuilder(MonitorServiceApplication.class);
            springApplicationBuilder.headless(false);
            applicationContext = springApplicationBuilder.run(runArgs);
            log.info("Spring 启动成功");
        } catch (Exception e) {
            log.error("Spring 启动失败:{}", e.getMessage(), e);
            ApplicationStatusContextHolder.setState(ApplicationState.STOPPED);
        }
    }

    public static void stopSpring() {
        log.info("开始停止 Spring");
        if (applicationContext != null) {
            applicationContext.close();
        }
        log.info("Spring 停止成功");
    }

    @Override
    public void init() throws Exception {
        Thread.setDefaultUncaughtExceptionHandler(new FxUncaughtExceptionHandler());
        H2Util.init();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> Platform.runLater(MonitorServiceApplication::close)));
    }

    @Override
    public void start(Stage stage) throws Exception {
        if (BooleanUtil.toBoolean(H2Util.getConfigValue(ConfigKeyConstant.AUTO_START))) {
            Platform.runLater(MonitorServiceApplication::startSpring);
        }
        Platform.setImplicitExit(false);
        MonitorSystemTray.show();
    }

    @Override
    public void stop() throws Exception {
        close();
    }

    public static void close() {
        if (applicationContext != null) {
            applicationContext.close();
        }
        if (applicationContext != null) {
            while (applicationContext.isActive()) {}
        }
        H2Util.close();
        Platform.exit();
        System.exit(0);
    }

    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        String portStr = H2Util.getConfigValue(ConfigKeyConstant.SERVER_PORT);
        if (StrUtil.isBlank(portStr)) {
            Dict dict = YamlUtil.load(ResourceUtil.getStream("application.yml"), Dict.class);
            portStr = dict.getByPath(ConfigKeyConstant.SERVER_PORT, String.class);
        }
        if (StrUtil.isBlank(portStr)) {
            portStr = "8080";
        }
        factory.setPort(Integer.parseInt(portStr));
    }
}
